﻿
Public Class slice5
    Inherits _slice(Of atom)

    Public Const datafolder As String = "slice5"
    Public Const datasuffix As String = ".data"

    Private Class fceslite
        Inherits freclust.fceslite(Of _false)

        Public Const INVALID_CLUSTERID As SByte = freclust.cluster.INVALID
        Public Const CLUSTERID_SIZE As Byte = freclust.cluster.CLUSTERID_SIZE

        Public Sub New(ByVal filename As String)
            MyBase.New(filename)
        End Sub
    End Class

    Public Class atom
        Public start As UInt64 = 0
        Public clusterIdStart As UInt64 = 0
        Public clusterId As Int64 = fceslite.INVALID_CLUSTERID

        Public Function [end]() As UInt64
            Return clusterIdStart + fceslite.CLUSTERID_SIZE
        End Function

        Public Function length() As UInt64
            Return [end]() - start
        End Function
    End Class

    Private f As fceslite = Nothing
    Private slicesize As UInt64
    Private _datafile As String = Nothing

    Private Function datafile() As String
        If _datafile Is Nothing Then
            Dim folder As String = Nothing
            folder = combinepath(datapath(), datafolder)
            createDirectory(folder)
            _datafile = combinepath(folder, filename() + datasuffix)
        End If
        Return _datafile
    End Function

    Private Sub writeClusterId(ByVal atom As atom)
        assert(Not atom Is Nothing)
        stream().Position = atom.clusterIdStart
        writer().Write(atom.clusterId)
    End Sub

    Private Function confirmClusterId(ByVal atom As atom, ByVal outputId As Int64) As Boolean
        If atom.clusterId <> outputId Then
            atom.clusterId = outputId
            writeClusterId(atom)
        End If
        Return True
    End Function

    Protected Overrides Function appendImpl(ByVal atom As atom, ByVal buff As bytes) As Boolean
        Dim outputkey As Int64
        If Not f.append(atom.clusterId, buff, outputkey) OrElse Not confirmClusterId(atom, outputkey) Then
            raiseError("cannot append data to " + Convert.ToString(atom.clusterId), errorHandle.errorType.exclamation)
            Return False
        End If
        Return True
    End Function

    Protected Overrides Function atom_ctor() As atom
        Return New atom()
    End Function

    Protected Overrides Function closeImpl() As Boolean
        If f Is Nothing Then
            Return False
        Else
            Return f.close()
        End If
    End Function

    Protected Overrides Function deleteImpl(ByVal atom As atom) As Boolean
        Dim movelen As Int64
        movelen = stream().Length() - atom.end()
        If movelen < 0 Then
            raiseError("cannot get enough data from slice " + filename() + ", invalid slice file.", _
                       errorHandle.errorType.exclamation)
            Return False
        ElseIf movelen > 0 Then
            If streammove(stream(), atom.end(), atom.start, movelen) <> movelen Then
                raiseError("connot move enough data from index file " + filename(), _
                           errorHandle.errorType.exclamation)
                Return False
            End If

            Dim action As traversal_atoms_action = Function(a As atom) As Boolean
                                                       If a.start > atom.start Then
                                                           a.start -= atom.length()
                                                           a.clusterIdStart -= atom.length()
                                                       End If
                                                       Return True
                                                   End Function
            traversal_atoms(action)
        End If
        stream().SetLength(atom.start + movelen)

        If Not f.delete(atom.clusterId) Then
            raiseError("cannot delete cluster " + Convert.ToString(atom.clusterId), errorHandle.errorType.exclamation)
            Return False
        End If

        Return True
    End Function

    Protected Overrides Function filter_slicetype_value() As String
        Return configNaming.filter.value.slicetype.slice5
    End Function

    Protected Overrides Function maxSupportVersion() As String
        Return "5"
    End Function

    Protected Overrides Function minSupportVersion() As String
        Return "5"
    End Function

    Protected Overrides Function modifyImpl(ByVal atom As atom, ByVal buff As bytes) As Boolean
        Dim outputkey As Int64
        If Not f.write(atom.clusterId, buff, outputkey) OrElse Not confirmClusterId(atom, outputkey) Then
            raiseError("cannot write data to " + Convert.ToString(atom.clusterId), errorHandle.errorType.exclamation)
            Return False
        End If
        Return True
    End Function

    Protected Overrides Function readImpl(ByVal atom As atom, ByRef buff As bytes) As Boolean
        If Not f.read(atom.clusterId, buff) Then
            raiseError("cannot read data from " + Convert.ToString(atom.clusterId), errorHandle.errorType.exclamation)
            Return False
        End If
        Return True
    End Function

    Protected Overrides Function readIndex(ByRef key As String, ByRef atom As atom, ByVal lastpos As UInt64) As Boolean
        atom = New atom()
        atom.start = lastpos
        If binaryReaderHelper.readString(reader(), key) Then
            atom.clusterIdStart = stream().Position()
            If binaryReaderHelper.readInt64(reader(), atom.clusterId) Then
                Return True
            End If
        End If

        Return False
    End Function

    Protected Overrides Function retireImpl() As Boolean
        Return create_empty_slice() AndAlso closeImpl() AndAlso deletefile(datafile()) AndAlso create_fceslite()
    End Function

    Protected Overrides Function sizeofImpl(ByVal atom As atom, ByRef size As UInt64) As Boolean
        Return f.sizeof(atom.clusterId, size)
    End Function

    Protected Overrides Function version() As String
        Return "5"
    End Function

    Protected Overrides Function writeImpl(ByVal key As String, ByVal atom As atom, ByVal buff As bytes) As Boolean
        If f.write(fceslite.INVALID_CLUSTERID, buff, atom.clusterId) Then
            stream.Seek(0, IO.SeekOrigin.End)
            atom.start = stream().Position()
            writer().Write(key)
            atom.clusterIdStart = stream().Position()
            writeClusterId(atom)
            Return True
        Else
            raiseError("cannot write data to new clusterId", errorHandle.errorType.exclamation)
            Return False
        End If
    End Function

    Private Function create_fceslite() As Boolean
        f = New fceslite(datafile())
        Return True
    End Function

    Protected Overrides Sub open_start()
        MyBase.open_start()
        create_fceslite()
    End Sub

    Protected Overrides Sub create_start()
        MyBase.create_start()
        create_fceslite()
    End Sub

    Public Sub New()
        MyBase.New()
        slicesize = read_slicesize()
    End Sub

    Protected Overrides Function fullImpl() As Boolean
        Return MyBase.fullImpl() OrElse f.virtdisk_size() >= slicesize
    End Function

    Protected Overrides Sub flush()
        MyBase.flush()
        If Not f Is Nothing Then
            f.flush()
        End If
    End Sub

    Protected Overrides Function healthImpl(ByVal needDetailedMessage As Boolean) As Boolean
        If MyBase.healthImpl(needDetailedMessage) Then
            If f Is Nothing Then
                If needDetailedMessage Then
                    raiseError("fceslite is nothing.", errorHandle.errorType.warning)
                End If
                Return False
            Else
                Return True
            End If
        Else
            Return False
        End If
    End Function
End Class
